package SubjectTree.Three;

import java.util.Deque;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;

import Utility.TreeNode;

public class BuildTree {

/**
 * 难度：中等
 * 
 * 106. 从中序与后序遍历序列构造二叉树
 * 	根据一棵树的中序遍历与后序遍历构造二叉树。
 * 	
 * 注意:
 * 	你可以假设树中没有重复的元素。
 * 	
 * 例如，给出
 * 	中序遍历 inorder = [9,3,15,20,7]
 * 	后序遍历 postorder = [9,15,7,20,3]
 * 	返回如下的二叉树：
 * 	    3
 * 	   / \
 * 	  9  20
 * 	    /  \
 * 	   15   7
 *
 * */
	
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		BuildTree bt = new BuildTree();
		System.out.println(bt.buildTree(new int[] {9,3,15,20,7}, new int[] {9,15,7,20,3}));
	}
	//自己写(递归)
	public TreeNode buildTree(int[] inorder, int[] postorder) {
		if(inorder.length == 0 || postorder.length == 0)return null;
		return traversal(inorder, postorder);
	}
	public TreeNode traversal(int[] inorder, int[] postorder) {
		if(postorder.length == 0)return null;
		
		// 后序遍历数组最后一个元素，就是当前的中间节点
		int rootValue = postorder[postorder.length - 1];
		TreeNode root = new TreeNode(rootValue);
		
		// 叶子节点
		if(postorder.length == 1)return root;
		
		// 找到中序遍历的切割点
		int delimiterIndex;
		for(delimiterIndex = 0;delimiterIndex<inorder.length;delimiterIndex++) {
			if(inorder[delimiterIndex] == rootValue)break;
		}
		
		// 切割中序数组
        // 左闭右开区间：[0, delimiterIndex)
		int[] leftInorder = new int[delimiterIndex];
		for(int i=0;i<delimiterIndex;i++) {
			leftInorder[i] = inorder[i];
		}
		// [delimiterIndex + 1, end)
		int[] rightInorder = new int[inorder.length - delimiterIndex - 1];
		for(int i=delimiterIndex+1,j=0;i<inorder.length;i++,j++) {
			rightInorder[j] = inorder[i];
		}
		
		// 切割后序数组
        // 依然左闭右开，注意这里使用了左中序数组大小作为切割点
        // [0, leftInorder.size)
		int[] leftPostorder = new int[leftInorder.length];
		for(int i=0;i<leftInorder.length;i++) {
			leftPostorder[i] = postorder[i];
		}
        // [leftInorder.size(), end)
		int[] rightPostorder = new int[postorder.length - 1 - leftInorder.length];
		for(int i=leftInorder.length,j=0;i<postorder.length-1;i++,j++) {
			rightPostorder[j] = postorder[i];
		}
		
//		System.out.println("leftInorder :");
//		for (int i : leftInorder) {
//			System.out.print(i+",");
//        }
//		System.out.println("");
//		System.out.println("rightInorder :");
//		for (int i : rightInorder) {
//			System.out.print(i+",");
//        }
//		System.out.println("");
//		System.out.println("leftPostorder :");
//		for (int i : leftPostorder) {
//			System.out.print(i+",");
//        }
//		System.out.println("");
//		System.out.println("rightPostorder :");
//		for (int i : rightPostorder) {
//			System.out.print(i+",");
//        }
//		System.out.println("");
		
		root.left = traversal(leftInorder,leftPostorder);
		root.right = traversal(rightInorder,rightPostorder);
		
		return root;
    }
	//方法一：递归
	int post_idx;
    int[] postorder;
    int[] inorder;
    Map<Integer, Integer> idx_map = new HashMap<Integer, Integer>();

    public TreeNode helper(int in_left, int in_right) {
        // 如果这里没有节点构造二叉树了，就结束
        if (in_left > in_right) {
            return null;
        }

        // 选择 post_idx 位置的元素作为当前子树根节点
        int root_val = postorder[post_idx];
        TreeNode root = new TreeNode(root_val);

        // 根据 root 所在位置分成左右两棵子树
        int index = idx_map.get(root_val);

        // 下标减一
        post_idx--;
        // 构造右子树
        root.right = helper(index + 1, in_right);
        // 构造左子树
        root.left = helper(in_left, index - 1);
        return root;
    }
    public TreeNode buildTree1(int[] inorder, int[] postorder) {
        this.postorder = postorder;
        this.inorder = inorder;
        // 从后序遍历的最后一个元素开始
        post_idx = postorder.length - 1;

        // 建立（元素，下标）键值对的哈希表
        int idx = 0;
        for (Integer val : inorder) {
            idx_map.put(val, idx++);
        }
        
        return helper(0, inorder.length - 1);
    }
    
    //方法二：迭代
    public TreeNode buildTree2(int[] inorder, int[] postorder) {
        if (postorder == null || postorder.length == 0) {
            return null;
        }
        // 从后序遍历的最后一个元素开始构建树
        TreeNode root = new TreeNode(postorder[postorder.length - 1]);
        Deque<TreeNode> stack = new LinkedList<TreeNode>();
        stack.push(root);
        int inorderIndex = inorder.length - 1;
        //遍历后序元素
        for (int i = postorder.length - 2; i >= 0; i--) {
            int postorderVal = postorder[i];
            TreeNode node = stack.peek();
            if (node.val != inorder[inorderIndex]) {
                node.right = new TreeNode(postorderVal);
                stack.push(node.right);
            } else {
                while (!stack.isEmpty() && stack.peek().val == inorder[inorderIndex]) {
                    node = stack.pop();
                    inorderIndex--;
                }
                node.left = new TreeNode(postorderVal);
                stack.push(node.left);
            }
        }
        return root;
    }
}
